﻿namespace RowCountsProvider.Tests
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Data.SqlClient;
    using System.IO;
    using System.Linq;
    using System.Security.Cryptography;

    using CloudNinja.Metering.Common;
    using CloudNinja.Metering.Common.Configuration;
    using CloudNinja.Metering.Common.Logging;
    using CloudNinja.Metering.Data.Models;
    using CloudNinja.Metering.Tests;
    using CloudNinja.Metering.Tests.Support;

    using CloundNinja.Metering.RowCountsProvider;
    using CloundNinja.Metering.RowCountsProvider.Configuration;

    using Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling;

    using Moq;

    using Xunit;

    public class RowCountsProviderTests
    {
        [Fact]
        public void CanRecordPoints()
        {
            var backingStore = new List<MeterData>();

            var meteringRepositoryMock = new MeteringRepositoryMock(backingStore);

            var loggerMock = new Mock<ILogger>();

            const string ConnectionString = "Data Source=.;Initial Catalog=cnmb;Integrated Security=SSPI;";

            var rowsResult = new List<TenantRowCountSpec>
                {
                    new TenantRowCountSpec { ServerAlias = "server1", ConnectionString = ConnectionString, TableName = "table1", TenantIdColumn = "tenantid", CountColumn = "value", TenantIdPattern = "tnt_{.+}"},
                    new TenantRowCountSpec { ServerAlias = "server2", ConnectionString = ConnectionString, TableName = "table2", TenantIdColumn = "tenantid", CountColumn = "value", TenantIdPattern = "tnt_{.+}"},
                    new TenantRowCountSpec { ServerAlias = "server3", ConnectionString = ConnectionString, TableName = "table3", TenantIdColumn = "tenantid", CountColumn = "value", TenantIdPattern = "tnt_{.+}"},
                };

            this.GenerateTenantData(ConnectionString, rowsResult);

            var meteringContextMock = new Mock<IMeteringContext>();
            var strategy = new FixedInterval("fixed", 10, TimeSpan.FromSeconds(3));

            var sqlRetryPolicy = new RetryPolicy<SqlDatabaseTransientErrorDetectionStrategy>(strategy);

            meteringContextMock.Setup(mc => mc.SqlAzureCommandRetryPolicy).Returns(sqlRetryPolicy);
            meteringContextMock.Setup(mc => mc.SqlAzureConnectionRetryPolicy).Returns(sqlRetryPolicy);

            var provider = new RowCountsProvider(
                TimeSpan.FromSeconds(1),
                meteringRepositoryMock,
                loggerMock.Object,
                meteringContextMock.Object,
                rowsResult.AsQueryable());

            provider.Initialize();
            provider.Collect();
            provider.Stop();

            loggerMock.Verify(l => l.LogInformation(It.IsAny<string>()), Times.Exactly(4));
            Assert.True(backingStore.Count == 3);
        }

        private void GenerateTenantData(string connectionString, IEnumerable<TenantRowCountSpec> tenantTables)
        {
            using (var connection = new SqlConnection(connectionString))
            {
                var createTableCommands = from tenantTable in tenantTables
                                          select
                                              new SqlCommand(
                                              string.Format(
                                                  @" IF NOT EXISTS(SELECT * FROM sysobjects WHERE name = '{0}' and xtype='U')
                                                      BEGIN                                                        
                                                        CREATE TABLE {0} ({1} [nvarchar](64), {2} [int])
                                                       END",
                                                  tenantTable.TableName,
                                                  tenantTable.TenantIdColumn,
                                                  tenantTable.CountColumn),
                                              connection);
                connection.Open();
                foreach (var tableCommand in createTableCommands)
                {
                    tableCommand.ExecuteNonQuery();
                }

                var insertCommands = from tenantTable in tenantTables
                                     select
                                         new SqlCommand(
                                         string.Format(
                                             @" INSERT INTO {0} ({1}, {2}) VALUES ('tnt_{0}', 1)",
                                             tenantTable.TableName,
                                             tenantTable.TenantIdColumn,
                                                  tenantTable.CountColumn),
                                         connection);
                foreach (var tableCommand in insertCommands)
                {
                    tableCommand.ExecuteNonQuery();
                }
            }
        }

        [Fact]
        public void CanCreateConfigSection()
        {
            var connectionString = "Data Source=.\\sqlexpress;Initial Catalog=meteringtest;Integrated Security=SSPI;";


            var meteringSection = new MeteringSection
            {
                CommonSettings =
                    new CommonSettingsElement
                    {
                        ConnectionStrings =
                            new ConnectionStringsCollection
                                        {
                                            new ConnectionStringElement
                                                { Name = "Common", Value = "UseDevelopmentStorage=true" }
                                        },
                        BlockConnectionString = "UseDevelopmentStorage=true"
                    },
                MeteringRepositorySettings =
                    new MeteringRepositorySettingsElement { APIKey = GlobalTestSettings.APIKey, UserId = "test" },
                ProviderSettings =
                {
                    Providers =
                        new ProvidersCollection
                                    {
                                        new TenantRowCountProviderSettingsElement
                                            {
                                                Type = typeof(RowCountsProvider).AssemblyQualifiedName,
                                                TenantRowCountProviderMeteringProviderSettings =
                                                    new TenantRowCountMeteringProviderSettings
                                                        {
                                                            DefaultTenantIdPattern = "tnt_{.+}",
                                                            StorageAccounts =
                                                                new TenantRowCountConnectionCollection()
                                                                    {
                                                                        new TenantRowCountConnectionCollectionElement()
                                                                            {
                                                                                ServerAlias = "Storage1",
                                                                                ConnectionString = connectionString,
                                                                                TenantIdPattern = "tn_{.+}",
                                                                                TableName = "table1",
                                                                                TenantIdColumn = "tenantid",
                                                                                CountColumn = "value"
                                                                            },
                                                                        new TenantRowCountConnectionCollectionElement()
                                                                            {
                                                                                ServerAlias = "Storage2",
                                                                                ConnectionString = connectionString,
                                                                                TenantIdPattern = "tn_{.+}",
                                                                                TableName = "table2",
                                                                                TenantIdColumn = "tenantid",
                                                                                CountColumn = "value"
                                                                            },
                                                                        new TenantRowCountConnectionCollectionElement()
                                                                            {
                                                                                ServerAlias = "Storage3",
                                                                                ConnectionString = connectionString,
                                                                                TenantIdPattern = "tn_{.+}",
                                                                                TableName = "table3",
                                                                                TenantIdColumn = "tenantid",
                                                                                CountColumn = "value"
                                                                            },
                                                                    }
                                                        }
                                            }
                                    }
                }
            };

            if (File.Exists("rowCounts.config"))
            {
                File.Delete("rowCounts.config");
            }

            this.CreateConfigFile("rowCounts", meteringSection);

        }

        private void CreateConfigFile(string configurationName, MeteringSection meteringSection)
        {
            var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            if (config.Sections["MeteringConfiguration"] == null)
            {
                config.Sections.Add("MeteringConfiguration", meteringSection);
            }

            meteringSection.SectionInformation.ForceSave = true;

            config.SaveAs(configurationName + ".config");
        }
    }
}
